using System;
using System.Text;
using System.Data;
using System.Collections;
using System.Collections.Generic;
using System.Web;
using System.IO;

using Framework;

namespace SchemaMonitor
{
    //Collection Class (Customisable half)
    public partial class CNodeList
    {
        #region Filters
        public CNode GetById(json.CNodeRoot rd) { return GetById(rd.Guid); }
		#endregion

		#region Aggregation
		#endregion


		#region Searching (Optional)
		public CNodeList Search(string nameOrId)
		{
			//1. Normalisation
			nameOrId = (nameOrId ?? string.Empty).Trim().ToLower();

			//2. Start with a complete list
			CNodeList results = this;

			//3. Use any available index, such as those generated for fk/bool columns
			//Normal Case - non-unique index (e.g. foreign key)
			//if (long.MinValue != userId) results = results.GetByUserId(userId);

			//Special case - unique index (e.g. primary key)
            if (!string.IsNullOrEmpty(nameOrId)) 
            {
                long id;
                if (long.TryParse(nameOrId, out id))
                {
					var g = json.CNodeRoot.Pad(id);
                    CNode obj = this.GetById(g);
                    if (null != obj)
                    {
                        results = new CNodeList(1);
                        results.Add(obj);
                        return results;
                    }
                }
            }

			//4. Exit early if remaining (non-index) filters are blank
			if (string.IsNullOrEmpty(nameOrId))
				return results;

			//5. Manually search each record using custom match logic, building a shortlist
			CNodeList shortList = new CNodeList();
			foreach (CNode i in results)
				if (Match(nameOrId, i))
					shortList.Add(i);
			return shortList;
		}
		//Manual Searching e.g for string-based columns i.e. anything not indexed (add more params if required)
		private bool Match(string name, CNode n)
		{
			return Match(name, n.Root);
		}
		private bool Match(string name, json.CNodeRoot r)
		{
			foreach (var i in r.Fields)
				if (i.Tag.FieldType.IsSystem)
					if (i.AsString.ToLower().Contains(name))
						return true;

			return false;   //If filter is active, reject any items that dont match
		}
		#endregion

		public new void Add(CNode item)
		{
			if (null != _index && !_index.ContainsKey(item.NodeGuid))
				_index[item.NodeGuid] = item;

			_indexByTypeId = null;
			_indexByUserId = null;
			_indexByDataIsGz = null;

			base.Add(item);
		}
		public new void Remove(CNode item)
		{
			if (null != _index && _index.ContainsKey(item.NodeGuid))
				_index.Remove(item.NodeGuid);
			_indexByTypeId = null;
			_indexByUserId = null;
			_indexByDataIsGz = null;
			base.Remove(item);
		}

		#region Cloning
		public CNodeList Clone(CDataSrc target) //, int parentId)
        {
            //No Transaction
            if (target is CDataSrcRemote)
                return Clone(target, null); //, parentId);

            //Transaction
            using (IDbConnection cn = target.Local.Connection())
            {
                IDbTransaction tx = cn.BeginTransaction();
                try
                {
                    CNodeList clone = Clone(target, tx); //, parentId);
                    tx.Commit();
                    return clone;
                }
                catch
                {
                    tx.Rollback();
                    throw;
                }
            }
        }
        public CNodeList Clone(CDataSrc target, IDbTransaction txOrNull) //, int parentId)
        {
            CNodeList list = new CNodeList(this.Count);
            foreach (CNode i in this)
                list.Add(i.Clone(target, txOrNull)); //, parentId));  *Child entities must reference the new parent
            return list;
        }
        #endregion
        
        #region Export to Csv
        //Note: For non-cached classes like this, should normally use CDataSrc.ExportToCsv(SelectWhere_DataSet)

        //Web 
        //Web - Need to add a project reference to System.Web, or comment out these two methods
        public void ExportToCsv(HttpResponse response) { ExportToCsv(response, "Nodes.csv"); }
        public void ExportToCsv(HttpResponse response, string fileName)
        {
            CDataSrc.ExportToCsv(response, fileName); //Standard response headers
            StreamWriter sw = new StreamWriter(response.OutputStream);
            ExportToCsv(sw);
            sw.Flush();
            response.End();
        }
        
        //Non-web
        public void ExportToCsv(string filePath)
        {
            StreamWriter sw = new StreamWriter(filePath);
            ExportToCsv(sw);
            sw.Close();
        }

        //Logic
        protected void ExportToCsv(StreamWriter sw)
        {
            string[] headings = new string[] { "NodeGuid", "NodeTypeId", "NodeUserId", "NodeData", "NodeDataIsGz", "NodeCreated"};
            CDataSrc.ExportToCsv(headings, sw);
            foreach (CNode i in this)
            {
                object[] data = new object[] {i.NodeGuid, i.NodeTypeId, i.NodeUserId, i.NodeData, i.NodeDataIsGz, i.NodeCreated};
                CDataSrc.ExportToCsv(data, sw);
            }
        }
        #endregion

        #region Preload Parent Objects

        //Efficiency Adjustment: Preloads the common parent for the whole list, to avoid database chatter 
        public CUser User
        {
            set
            {
                foreach (CNode i in this) i.User = value;
            }
        }
        #endregion


        #region Members
        private CTypeList _types;
        private List<int> _typeIds;
        #endregion 

        #region Resolve Associative table (and sort)
        public CTypeList Types
        {
            get
            {
                if (_types == null)
                {
                    lock (this)
                    {
                        if (null == _types)
                        {
                            _types = new CTypeList(this.Count);
                            foreach (CNode i in this)
                                if (i.Type != null)
                                    _types.Add(i.Type);
                            _types.Sort();
                        }
                    }
                }
                return _types;
            }
        }
        public CTypeList RemainingTypes(string search)
        {
            CTypeList temp = new CTypeList(CType.Cache.Search(search));
            temp.Remove(this.Types);
            return temp;
        }
        #endregion

        #region Resolve/Isolate PKs
        public List<int> TypeIds
        {
            get
            {
                if (null == _typeIds)
                {
                    lock (this)
                    {
                        if (null == _typeIds)
                        {
                            _typeIds = new List<int>(this.Count);
                            foreach (CNode i in this)
                                _typeIds.Add(i.NodeTypeId);
                        }
                    }
                }
                return _typeIds;
            }
        }
        #endregion


        #region Custom

        public json.CNodeRootList AsJson(CNodeList known, long userId)
        {
            var list = new json.CNodeRootList(this.Count, known, userId);
            foreach (var i in this)
                list.Add(i.Root);
            return list;
        }
        public CChangeSet Diff(json.CNodeRootList latest)
        {
            var changes = new CChangeSet();
            foreach (var i in this)
            {
                var older = i.Root;
                var fresh = latest.GetById(i.NodeGuid);
                if (null == fresh)
                    continue;

                var diff = new CDiffList(older, fresh);
                if (diff.Count > 0)
                    changes.Add(i, diff);
            }
            return changes;
        }
		#endregion




		//Index by Date
		public CNodeList GetByTicks(long ticks)
		{
			CNodeList temp = null;
			if (!IndexByTicks.TryGetValue(ticks, out temp))
			{
				temp = new CNodeList();
				IndexByTicks[ticks] = temp;
			}
			return temp;
		}

		[NonSerialized]
		private Dictionary<long, CNodeList> _indexByTicks;
		public Dictionary<long, CNodeList> IndexByTicks
		{
			get
			{
				if (null == _indexByTicks)
				{
					Dictionary<long, CNodeList> index = new Dictionary<long, CNodeList>();
					CNodeList temp = null;
					foreach (CNode i in this)
					{
						var t = i.Root.CreatedTime.Ticks;
						if (!index.TryGetValue(t, out temp))
						{
							temp = new CNodeList();
							index[t] = temp;
						}
						temp.Add(i);
					}
					_indexByTicks = index;
				}
				return _indexByTicks;
			}
		}


	}
}
